<template>
  <v-navigation-drawer
    v-model="layoutStore.showGroupDrawer"
    color="background"
    floating
    location="top"
    rounded="lg"
    temporary
    :width="drawerSize"
  >
    <v-sheet class="d-flex flex-column gap-4" color="transparent">
      <v-sheet
        ref="headerEl"
        class="d-flex align-center justify-space-between px-3 py-2 position-sticky top-0 z-10"
        elevation="2"
      >
        <div class="d-flex align-center">
          <v-avatar class="mr-2" size="20" :style="{ background: groupColor }"></v-avatar>
          <span class="text-body-2 font-weight-medium mr-2">{{ groupTitle }}</span>
          <div class="d-flex align-center gap-2">
            <v-chip class="mr-1" color="primary" size="small" variant="tonal">
              {{ t('groupInspectorDrawer.chips.tabs', { n: tabs.length }) }}
            </v-chip>
            <v-chip color="secondary" size="small" variant="tonal">
              {{ t('groupInspectorDrawer.chips.waiting', { n: waitingTabs.length }) }}
            </v-chip>
          </div>
        </div>
        <div class="d-flex align-center">
          <MenuSelect
            button-class="text-no-wrap"
            color="success"
            datatestid="sameDomainDelay"
            :icon="mdiTimerOutline"
            :items="sameDomainDelayOptions"
            :menu-min-width="160"
            :model-value="tabStore.sameDomainDelaySec"
            :text="t('groupInspectorDrawer.sameDomainDelay', { n: tabStore.sameDomainDelaySec })"
            variant="tonal"
            @update:model-value="val => (tabStore.sameDomainDelaySec = val)"
          >
            <template #item="{ item }">
              <v-list-item-title>{{
                t('groupInspectorDrawer.units.seconds', { n: Number(item.title) })
              }}</v-list-item-title>
            </template>
          </MenuSelect>
          <v-tooltip location="bottom" open-delay="150">
            <template #activator="{ props }">
              <v-btn
                v-bind="props"
                class="ml-1 mr-3"
                density="comfortable"
                icon
                size="small"
                variant="text"
              >
                <v-icon :icon="mdiHelpCircleOutline" />
              </v-btn>
            </template>
            <div class="text-body-2" v-html="t('groupInspectorDrawer.helpHtml')"></div>
          </v-tooltip>

          <MenuSelect
            button-class="text-no-wrap"
            color="success"
            datatestid="crossDomainLimit"
            :icon="mdiLan"
            :items="crossDomainAtOnceOptions"
            :menu-min-width="180"
            :model-value="tabStore.maxCrossDomainConcurrency"
            :text="
              t('groupInspectorDrawer.crossDomainLimit', { n: tabStore.maxCrossDomainConcurrency })
            "
            variant="tonal"
            @update:model-value="val => (tabStore.maxCrossDomainConcurrency = val)"
          >
            <template #item="{ item }">
              <v-list-item-title>{{
                t('groupInspectorDrawer.units.count', { n: Number(item.title) })
              }}</v-list-item-title>
            </template>
          </MenuSelect>

          <v-btn
            class="ml-4"
            color="secondary"
            data-testid="groupInspectorDrawer-retryFailed"
            :disabled="retryingFailed || failedTabsCount === 0"
            :loading="retryingFailed"
            :prepend-icon="mdiRefresh"
            size="small"
            variant="tonal"
            @click="onRetryFailed"
            >{{ t('groupInspectorDrawer.actions.retryFailed') }}</v-btn
          >

          <v-btn
            class="ml-2"
            color="secondary"
            data-testid="groupInspectorDrawer-closeAll"
            :prepend-icon="mdiCloseBoxMultiple"
            size="small"
            :title="t('groupInspectorDrawer.actions.closeAllTitle')"
            variant="tonal"
            @click="onCloseGroup"
            >{{ t('groupInspectorDrawer.actions.closeAll') }}</v-btn
          >
          <v-btn
            color=""
            data-testid="groupInspectorDrawer-closeGroupInspector"
            icon
            variant="text"
            @click="layoutStore.closeGroupInspector()"
          >
            <v-icon :icon="mdiClose" />
          </v-btn>
        </div>
      </v-sheet>

      <!-- Unified grid: active + waiting together -->

      <v-container class="py-3" fluid>
        <v-row ref="gridRowEl" dense>
          <v-slide-y-transition group leave-absolute mode="in-out">
            <v-col
              v-for="(it, index) in items"
              :key="it.key"
              cols="12"
              :data-index="index"
              lg="4"
              md="6"
              sm="6"
              xl="3"
              xxl="2"
            >
              <GroupTabItem
                v-if="it.type === 'tab'"
                :datatestid="index"
                kind="tab"
                :tab="it.t"
                @activate="() => tabStore.setActiveTab(it.t.id)"
                @close="() => tabStore.closeTabs([it.t.id])"
                @refresh="() => tabHandler.refresh(it.t.webContentsId)"
              />
              <GroupTabItem
                v-else
                kind="waiting"
                :waiting="it.w"
                @open="() => onOpenSpecificWaiting(it.w.id)"
                @remove="() => onRemoveWaiting(it.w.id)"
              />
            </v-col>
          </v-slide-y-transition>
          <v-col v-if="items.length === 0" cols="12">
            <v-alert density="compact" type="info" variant="tonal">{{
              t('groupInspectorDrawer.empty')
            }}</v-alert>
          </v-col>
        </v-row>
      </v-container>
    </v-sheet>
  </v-navigation-drawer>
</template>

<script lang="ts" setup>
import type { ComponentPublicInstance } from 'vue'
import { computed, ref, watch } from 'vue'
import { useDisplay } from 'vuetify'

import { useI18n } from 'vue-i18n'

import {
  mdiClose,
  mdiCloseBoxMultiple,
  mdiHelpCircleOutline,
  mdiLan,
  mdiRefresh,
  mdiTimerOutline
} from '@mdi/js'
import GroupTabItem from './GroupTabItem.vue'
import { useTabStore } from '@/windows/layoutBrowser/stores/tab'
import { scheduleByDomain } from '@/utils/domainScheduler'

import { getGroupColorById, openGroupNewTab } from '@/utils/tab-group'

import { useLayoutStore } from '@/windows/layoutBrowser/stores/layout'
import { extractDomain } from '~shared/utils/urlUtils'

import MenuSelect from '@/components/common/MenuSelect.vue'

const { t } = useI18n()

const layoutStore = useLayoutStore()

const display = useDisplay()

const headerEl = ref<ComponentPublicInstance | null>(null)
const gridRowEl = ref<ComponentPublicInstance | null>(null)
const measuredHeader = ref(56)
const measuredItem = ref(99)

const columnsPerRow = computed(() => {
  // 匹配 v-col 的断点设置：cols="12" lg="4" md="6" sm="6" xl="3" xxl="2"
  let span
  if (display.xxl.value) span = 2
  else if (display.xl.value) span = 3
  else if (display.lg.value) span = 4
  else if (display.md.value) span = 6
  else if (display.sm.value) span = 6
  else span = 12
  return Math.max(1, Math.floor(12 / span))
})

const tabHandler = _pre_.tab
const retryingFailed = ref(false)
const failedTabsCount = computed(() => (group.value?.tabs || []).filter(t => t.state?.error).length)

const tabStore = useTabStore()
const groupId = computed(() => {
  return layoutStore.groupParams?.groupId
})

const sameDomainDelayOptions = computed(() =>
  Array.from({ length: 7 }, (_, i) => ({ title: String(i), value: i }))
)
const crossDomainAtOnceOptions = computed(() =>
  Array.from({ length: 6 }, (_, i) => ({ title: String(i + 1), value: i + 1 }))
)

const group = computed<F2.tab.Group | undefined>(() => {
  const arr = tabStore.tabs

  return arr.find(
    (g): g is F2.tab.Group => 'tabs' in g && g.type === 'group' && g.id === groupId.value
  )
})

const groupTitle = computed(
  () => group.value?.title || t('groupInspectorDrawer.groupTitle', { id: groupId.value })
)
const groupColor = computed(() => getGroupColorById(groupId.value))

const tabs = computed(() => group.value?.tabs || [])
const waitingTabs = computed(() => group.value?.waitingTabs || [])

const items = computed(() => {
  const active = (tabs.value || []).map(t => ({
    type: 'tab' as const,
    key: t.id,
    title: t.title || t.url,
    url: t.url,
    t
  }))
  const waiting = (waitingTabs.value || []).map(w => ({
    type: 'waiting' as const,
    key: w.id,
    title: w.title || w.url,
    url: w.url,
    w
  }))
  return [...active, ...waiting]
})

function onOpenSpecificWaiting(waitingId: number) {
  if (!group.value) return
  const idx = group.value.waitingTabs.findIndex(t => t.id === waitingId)
  if (idx > 0) {
    const [picked] = group.value.waitingTabs.splice(idx, 1)
    if (picked) group.value.waitingTabs.unshift(picked)
  }
  openGroupNewTab(group.value, { maxConcurrent: tabStore.maxCrossDomainConcurrency })
}

// 使用按域调度器重试失败标签：跨域并行、同域串行并带延迟

async function onRetryFailed() {
  if (!group.value || retryingFailed.value) return
  retryingFailed.value = true
  try {
    const failed = group.value.tabs.filter(t => t.state?.error && t.url)
    await scheduleByDomain(
      failed,
      t => extractDomain(t.url) || 'unknown',
      async t => {
        if (t.webContentsId) tabHandler.refresh(t.webContentsId)
      },
      {
        maxDomainsAtOnce: Math.max(1, Number(tabStore.maxCrossDomainConcurrency || 1)),
        sameDomainDelayMs: Math.max(0, Number(tabStore.sameDomainDelaySec || 0)) * 1000
      }
    )
  } finally {
    retryingFailed.value = false
  }
}

function onRemoveWaiting(waitingId: number) {
  if (!group.value) return
  group.value.waitingTabs = group.value.waitingTabs.filter(t => t.id !== waitingId)
}

const rows = computed(() => {
  const count = items.value.length
  const cols = columnsPerRow.value
  return Math.max(1, Math.ceil(count / cols))
})

const drawerSize = computed(() => {
  const min = 180
  const max = Math.floor(display.height.value * 0.5)
  const padding = 24 // v-container 的 py-3 约等于上下各 12px
  const content = rows.value * (measuredItem.value || 108)
  const total = (measuredHeader.value || 56) + padding + content
  return Math.min(max, Math.max(min, total))
})

function onCloseGroup() {
  if (!group.value) return
  const ids = group.value.tabs.map(t => t.id)
  group.value.waitingTabs = []
  tabStore.closeTabs(ids)
}

watch(
  () => items.value.length,
  () => {
    if (items.value.length === 0) {
      layoutStore.closeGroupInspector()
    }
  }
)
</script>

<i18n>
{
  "zhHans": {
    "groupInspectorDrawer": {
      "sameDomainDelay": "同域延迟{n}秒",
      "crossDomainLimit": "跨域同时打开{n}个",
      "helpHtml": "<div><strong>什么是“域”</strong>：我们按 URL 的 hostname（主机名）识别。例如：<div >同域：docs.example.com/a 与 docs.example.com/b</div><div >跨域：docs.baidu.com 与 mail.google.com</div><div >注意：www.example.com 与 example.com 也算跨域</div></div><div class=\"mt-1\"><strong>为什么需要同域延迟</strong>：<br/>同域同时/高频打开容易触发登录/会话竞争、频率限制或风控策略，甚至有被封号/封IP的风险；<br />适当延迟可降低触发概率、提高成功率，并减少拦截与资源抢占。如无需延迟，可选 0 秒。</div>",
      "groupTitle": "分组 {id}",
      "chips": {
        "tabs": "标签 {n}",
        "waiting": "等待 {n}"
      },
      "units": {
        "seconds": "{n}秒",
        "count": "{n}个"
      },
      "actions": {
        "retryFailed": "重试失败",
        "closeAllTitle": "关闭分组内全部活动标签",
        "closeAll": "关闭全部"
      },
      "empty": "无内容"
    }
  },
  "en": {
    "groupInspectorDrawer": {
      "sameDomainDelay": "Delay on same domain {n}s",
      "crossDomainLimit": "Cross-domain open at once: {n}",
      "helpHtml": "<div><strong>What is a domain</strong>: We use the URL hostname as the domain. For example:<div >Same domain: docs.example.com/a and docs.example.com/b</div><div >Cross-domain: docs.baidu.com and mail.google.com</div><div >Note: www.example.com and example.com are also considered cross-domain</div></div><div class=\"mt-1\"><strong>Why delay on the same domain</strong>: <br />Opening many pages on the same domain at the same time or at a high rate can trigger login/session conflicts,<br /> rate limits, or anti-bot rules, even account/IP bans.<br /> Adding a small delay reduces risk and improves success. Set 0 if you don't need delays.</div>",
      "units": {
        "seconds": "{n}s",
        "count": "{n}"
      },
      "groupTitle": "Group {id}",
      "chips": {
        "tabs": "Tabs {n}",
        "waiting": "Waiting {n}"
      },
      "actions": {
        "retryFailed": "Retry failed",
        "closeAllTitle": "Close all active tabs in this group",
        "closeAll": "Close all"
      },
      "empty": "No content"
    }
  }
}
</i18n>

<style scoped>
.text-no-wrap {
  white-space: nowrap;
}
</style>
